#include "precomp.h"

#include "../Entity/Entity.h"

namespace Tmpl {

// Empire System Setup
static DataValuePool<Empire> gEmpires;
void EmpireSystem::Init()
{
	gEmpires.Init(32);
}
void EmpireSystem::Clear()
{
	gEmpires.Clear();
}
int EmpireSystem::GetEmpireCount()
{
	return gEmpires.GetValueCount();
}
int EmpireSystem::NewEmpire(const char* race)
{
	int id = gEmpires.NewValue();
	Empire* value = gEmpires.GetValue(id);
	strcpy_s(value->mRace,32,race);

	// Initialise Lua Script
	value->mLuaState = lua_open();
	luaL_openlibs(value->mLuaState);
	LuaRegisterEmpireFunctions(value->mLuaState);

	// Load the file
	char fileName[128];
	sprintf_s(fileName, "Data/Empires/%s/main.lua", race);
    if (luaL_loadfile(value->mLuaState, fileName) || lua_pcall(value->mLuaState, 0, 0, 0)) 
    {
		Log::Print("Error loading file %s: %s\n", fileName, lua_tostring(value->mLuaState, -1));
	}

	return id;
}
void EmpireSystem::RemoveEmpire(int id)
{
	Empire* value = gEmpires.GetValue(id);
	lua_close(value->mLuaState);
	gEmpires.RemoveValue(id);
}
Empire* EmpireSystem::GetEmpire(int id)
{
	return gEmpires.GetValue(id);
}
// Empire
UnitData Empire::GetUnitData(const char* unitName, int level)
{
	//mLuaStateog::Print("UnitTypes - Getting Data for unit: %s, level %d", unitName, level);
	UnitData data;
	data.flags = 0;
	strcpy_s(data.name, 16, unitName);

	// Get the function
    lua_getglobal(mLuaState, unitName);

	// Execute the function
    if(!lua_isfunction(mLuaState,-1))
    {
        lua_pop(mLuaState,1);
		Log::Print("UnitTypes - Error GetUnitData %s, not a valid function!\n", unitName);
		return data;
    }

    // do the call (x arguments, x results)
	lua_pushinteger(mLuaState, level);
    if (lua_pcall(mLuaState, 1, 1, 0) != 0) {
        Log::Print("UnitTypes - Error running GetUnitData %s: %s\n", unitName, lua_tostring(mLuaState, -1));
		return data;
    }

	// read back the table
	if (!lua_istable(mLuaState, -1))
	{
        Log::Print("UnitTypes - Error GetUnitData %s, result is not a table!\n", unitName);
		return data;
	}

	// Solve the flags
	const char* t = LuaTableGetString(mLuaState, "type", 1);
	if (!_strcmpi(t, "unit")) data.flags |= Entity::ENTITY_UNIT;
	if (!_strcmpi(t, "building")) data.flags |= Entity::ENTITY_BUILDING;
	if (!_strcmpi(t, "hero")) data.flags |= Entity::ENTITY_HERO;
	if (!_strcmpi(t, "tree")) data.flags |= Entity::ENTITY_TREE | Entity::ENTITY_DOODADD;
	if (!_strcmpi(t, "doodadd")) data.flags |= Entity::ENTITY_DOODADD;
	if (!_strcmpi(t, "projectile")) data.flags |= Entity::ENTITY_PROJECTILE;

	// Get the stats
	strcpy_s(data.model, LuaTableGetString(mLuaState, "model",  1));

	if (data.flags & Entity::ENTITY_BUILDING)
	{
		data.building.hp = float(LuaTableGetNumber(mLuaState, "hp",  1));
		strcpy_s(data.building.collisionFunc, 32, LuaTableGetString(mLuaState, "collision",  1));
	}
	else if (data.flags & Entity::ENTITY_UNIT)
	{
		data.unit.hp = float(LuaTableGetNumber(mLuaState, "hp",  1));
		data.unit.size = float(LuaTableGetNumber(mLuaState, "size",  1)) * 0.02f;
		data.unit.speed = float(LuaTableGetNumber(mLuaState, "speed",  1)) * 0.001f;
	}
	else if (data.flags & Entity::ENTITY_PROJECTILE)
	{
		data.projectile.flightTime = float(LuaTableGetNumber(mLuaState, "flightTime",  1));
		data.projectile.dmg = float(LuaTableGetNumber(mLuaState, "damage",  1));
		data.projectile.areaOfEffect = float(LuaTableGetNumber(mLuaState, "areaOfEffect",  1)) * 0.1f;
		data.projectile.speed = float(LuaTableGetNumber(mLuaState, "speed",  1)) * 0.001f;
	}
	else if (data.flags & Entity::ENTITY_DOODADD)
	{
		data.doodadd.hp = float(LuaTableGetNumber(mLuaState, "hp",  1));
		data.doodadd.size = float(LuaTableGetNumber(mLuaState, "size",  1)) * 0.02f;
	}

	// return
	lua_pop(mLuaState, 1);
	return data;
}
void Empire::ExecCollisionFunc(const char* func, int buildingId)
{
	// Get the function
    lua_getglobal(mLuaState, func);

	// Execute the function
    if(!lua_isfunction(mLuaState,-1)){ Log::Print("Empire - Error ExecCollisionFunc %s, not a valid function!\n", func); return; }
	lua_pushinteger(mLuaState, buildingId);
    if (lua_pcall(mLuaState, 1, 0, 0) != 0) 
	{
        Log::Print("Empire - Error ExecCollisionFunc %s: %s\n", func, lua_tostring(mLuaState, -1));
    }
}
// Updating
void EmpireSystem::UpdateEmpire(int empire)
{
	if (empire >= gEmpires.GetValueCount()) return;

	// Execute the on update callback
	Empire* v = gEmpires.GetUnmappedValue(empire);
	lua_getglobal(v->mLuaState, "Callback_On_Update");
    if(!lua_isfunction(v->mLuaState,-1))
    {
		Log::Print("Empire - Error running Callback_On_Update, not a valid function!\n");
    }
	else if (lua_pcall(v->mLuaState, 0, 0, 0) != 0) 
	{
		Log::Print("Empire - Error running Callback_On_Updat: %s\n", lua_tostring(v->mLuaState, -1));
    }
}
JobHandle EmpireSystem::Update(JobHandle dependency)
{
	JobHandle empireUpdate = JobManager::NewJob();
	int count = gEmpires.GetValueCount();
	for (int i=0; i<count; ++i)
	{
		JobHandle job = JobManager::NewJob(&EmpireSystem::UpdateEmpire,i);
		JobManager::AddJobDependency(job, dependency);
		JobManager::AddExecuteJob(job);

		JobManager::AddJobDependency(empireUpdate, job);
	}
	JobManager::AddExecuteJob(empireUpdate);
	return empireUpdate;
}
}